package tech

func (g *Grid) aicGetWeaks(node *ChainNode) []GroupItem {
	weaks := []GroupItem{}
	for _, cell := range g.candSeenCells(node.item.cand, node.item.group[0]) {
		weak := GroupItem{node.item.cand, [9]*Cell{cell}}
		//if existSingleItemCell(node, weak) {
		//	continue
		//}
		weaks = append(weaks, weak)
	}
	if node.pre == nil || (node.pre != nil && node.pre.item.group[0] != node.item.group[0]) {
		for _, cand := range node.item.group[0].cands.GetList() {
			if cand == node.item.cand {
				continue
			}
			weak := GroupItem{cand, node.item.group}
			weaks = append(weaks, weak)
		}
	}
	return weaks
}

func (g *Grid) aicGetStrongs(node *ChainNode) []GroupItem {
	//n := node
	//for n.pre != nil {
	//	n = n.pre
	//}
	//head := n.item.cell
	strongs := []GroupItem{}
	for _, cell := range g.candSeenCells(node.item.cand, node.item.group[0]) {
		houses := g.getSameHouses([]*Cell{node.item.group[0], cell})
		if !isOnlyInHouses(node.item.cand, []*Cell{cell, node.item.group[0]}, houses) {
			continue
		}
		strong := GroupItem{node.item.cand, [9]*Cell{cell}}
		//if cell != head && existSingleItemCell(node, strong) {
		//	continue
		//}
		strongs = append(strongs, strong)
	}
	if node.item.group[0].cands.Count() == 2 &&
		(node.pre == nil || (node.pre != nil && node.pre.item.group[0] != node.item.group[0])) {
		for _, cand := range node.item.group[0].cands.GetList() {
			if cand == node.item.cand {
				continue
			}
			strong := GroupItem{cand, node.item.group}
			strongs = append(strongs, strong)
		}
	}
	return strongs
}

func oneOrTwoCellCloseCycle(head, tail GroupItem) bool {
	if head.cand == tail.cand {
		return isSameHouse([]*Cell{head.group[0], tail.group[0]})
	} else {
		return head.group[0] == tail.group[0]
	}
}

func (g *Grid) canRemoveAICSingle(items []GroupItem) (rmItems []GroupItem) {
	head := items[1]
	tail := items[len(items)-1]
	if head.group[0] == tail.group[0] {
		for _, cand := range head.group[0].cands.GetList() {
			if cand == head.cand || cand == tail.cand {
				continue
			}
			rmItems = append(rmItems, GroupItem{cand, head.group})
		}
	} else if head.cand == tail.cand {
		rmCells := g.candCellsSeenCells(head.cand, []*Cell{head.group[0], tail.group[0]})
		for _, cell := range rmCells {
			rmItems = append(rmItems, GroupItem{head.cand, [9]*Cell{cell}})
		}
	} else {
		rmItems = append(rmItems, items[0])
	}

	return
}

func (s *Solve) AIC3(g *Grid) (used bool, results []*Result) {
	c := Chain{
		g:            g,
		strategyName: "Alternating Inference Chain 3",
		getWeaks:     g.aicGetWeaks,
		getStrongs:   g.aicGetStrongs,
		closeCycle:   oneOrTwoCellCloseCycle,
		canRemove:    g.canRemoveAICSingle,
	}

	return c.ExecStrategy3()
}

func (s *Solve) AIC2(g *Grid) (used bool, results []*Result) {
	c := Chain{
		g:            g,
		strategyName: "Alternating Inference Chain 2",
		getWeaks:     g.aicGetWeaks,
		getStrongs:   g.aicGetStrongs,
		closeCycle:   g.metWithStrongLink,
		canRemove:    g.canRemoveAICSingle,
	}

	return c.ExecStrategy2()
}
